{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1.读取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "from time import time\n",
    "import numpy as np\n",
    "from sklearn.datasets import load_digits\n",
    "from sklearn.model_selection import train_test_split\n",
    "diseaseResults = np.loadtxt('data/heart.csv', delimiter=',')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "diseasex = diseaseResults[:, 0:-1]\n",
    "diseasey = diseaseResults[:, -1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2.数据预处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((212, 13), (212,), (91, 13), (91,))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 数据集分割\n",
    "from sklearn.model_selection import train_test_split\n",
    "trainX, testX, trainY, testY = train_test_split(diseasex, diseasey, test_size = 0.3, random_state = 32)\n",
    "trainY = trainY.astype('int64')\n",
    "testY = testY.astype('int64')\n",
    "trainX.shape, trainY.shape, testX.shape, testY.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.preprocessing import StandardScaler\n",
    "s = StandardScaler()\n",
    "trainX = s.fit_transform(trainX)\n",
    "testX = s.transform(testX)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainY_mat = np.zeros((len(trainY), 2))\n",
    "trainY_mat[np.arange(0, len(trainY), 1), trainY] = 1\n",
    "\n",
    "testY_mat = np.zeros((len(testY), 2))\n",
    "testY_mat[np.arange(0, len(testY), 1), testY] = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((212, 2), (91, 2))"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "trainY_mat.shape, testY_mat.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.参数初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def initialize(h, K):\n",
    "    '''\n",
    "    参数初始化\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    h: int: 隐藏层单元个数\n",
    "    \n",
    "    K: int: 输出层单元个数\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    parameters: dict，参数，键是\"W1\", \"b1\", \"W2\", \"b2\"\n",
    "    \n",
    "    '''\n",
    "    np.random.seed(32)\n",
    "    W_1 = np.random.normal(size = (trainX.shape[1], h)) * 0.01\n",
    "    b_1 = np.zeros((1, h))\n",
    "    \n",
    "    np.random.seed(32)\n",
    "    W_2 = np.random.normal(size = (h, K)) * 0.01\n",
    "    b_2 = np.zeros((1, K))\n",
    "    \n",
    "    parameters = {'W1': W_1, 'b1': b_1, 'W2': W_2, 'b2': b_2}\n",
    "    \n",
    "    return parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4.前向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def linear_combination(X, W, b):\n",
    "    '''\n",
    "    计算Z，Z = XW + b\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    X: np.ndarray, shape = (n, m)，输入的数据\n",
    "    \n",
    "    W: np.ndarray, shape = (m, h)，权重\n",
    "    \n",
    "    b: np.ndarray, shape = (1, h)，偏置\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    Z: np.ndarray, shape = (n, h)，线性组合后的值\n",
    "    \n",
    "    '''\n",
    "    \n",
    "    # Z = XW + b\n",
    "    # YOUR CODE HERE\n",
    "    Z = np.dot(X,W) + b\n",
    "    \n",
    "    return Z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**ReLU激活函数**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def ReLU(X):\n",
    "    '''\n",
    "    ReLU激活函数\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    X: np.ndarray，待激活的矩阵\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    activations: np.ndarray, 激活后的矩阵\n",
    "    \n",
    "    '''\n",
    "    \n",
    "    # YOUR CODE HERE\n",
    "    activations = X.copy()\n",
    "    activations[activations < 0] = 0\n",
    "    return activations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**softmax激活**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_softmax(O):\n",
    "    '''\n",
    "    softmax激活\n",
    "    '''\n",
    "    # YOUR CODE HERE\n",
    "    softmax = np.exp(O) / np.exp(O).sum()\n",
    "    \n",
    "    return softmax"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(O):\n",
    "    '''\n",
    "    softmax激活函数\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    O: np.ndarray，待激活的矩阵\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    activations: np.ndarray, 激活后的矩阵\n",
    "    \n",
    "    '''\n",
    "    \n",
    "    # YOUR CODE HEER\n",
    "    tmp = O - np.max(O)\n",
    "    activations = np.exp(tmp) / np.exp(tmp).sum(axis = 1, keepdims = True)\n",
    "    \n",
    "    return activations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def log_softmax(x):\n",
    "    '''\n",
    "    log softmax\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    x: np.ndarray，待激活的矩阵\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    log_activations: np.ndarray, 激活后取了对数的矩阵\n",
    "    \n",
    "    '''\n",
    "    # YOUR CODE HERE\n",
    "    log_activations = x - np.max(x) - np.log(np.sum(np.exp(x - np.max(x)),axis = 1, keepdims = True))\n",
    "    \n",
    "    return log_activations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**交叉熵损失函数**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cross_entropy_with_softmax(y_true, O):\n",
    "    '''\n",
    "    求解交叉熵损失函数，这里需要使用log softmax，所以参数分别是真值和未经softmax激活的输出值\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    y_true: np.ndarray，shape = (n, K), 真值\n",
    "    \n",
    "    O: np.ndarray, shape = (n, K)，softmax激活前的输出层的输出值\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    loss: float, 平均的交叉熵损失值\n",
    "    \n",
    "    '''\n",
    "    \n",
    "    # 平均交叉熵损失\n",
    "    # YOUR CODE HERE\n",
    "    loss = - np.sum(log_softmax(O) * y_true) / len(y_true)\n",
    "    \n",
    "    return loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**正是因为$\\rm softmax$激活与交叉熵损失会有这样的问题，所以在很多深度学习框架中，交叉熵损失函数就直接带有了激活的功能，所以我们在实现前向传播计算的时候，就不要加$\\rm softmax$激活函数了。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def forward(X, parameters):\n",
    "    '''\n",
    "    前向传播，从输入一直到输出层softmax激活前的值\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    X: np.ndarray, shape = (n, m)，输入的数据\n",
    "    \n",
    "    parameters: dict，参数\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    O: np.ndarray, shape = (n, K)，softmax激活前的输出层的输出值\n",
    "    \n",
    "    '''\n",
    "    # 输入层到隐藏层\n",
    "    # YOUR CODE HERE\n",
    "    Z = linear_combination(X, parameters['W1'], parameters['b1'])\n",
    "    \n",
    "    # 隐藏层的激活\n",
    "    # YOUR CODE HERE\n",
    "    H = ReLU(Z)\n",
    "    \n",
    "    # 隐藏层到输出层\n",
    "    # YOUR CODE HERE\n",
    "    O = linear_combination(H, parameters['W2'], parameters['b2'])\n",
    "\n",
    "    return O"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_gradient(y_true, y_pred, H, Z, X, parameters):\n",
    "    '''\n",
    "    计算梯度\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    y_true: np.ndarray，shape = (n, K), 真值\n",
    "    \n",
    "    y_pred: np.ndarray, shape = (n, K)，softmax激活后的输出层的输出值\n",
    "    \n",
    "    H: np.ndarray, shape = (n, h)，隐藏层激活后的值\n",
    "    \n",
    "    Z: np.ndarray, shape = (n, h), 隐藏层激活前的值\n",
    "    \n",
    "    X: np.ndarray, shape = (n, m)，输入的原始数据\n",
    "    \n",
    "    parameters: dict，参数\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    grads: dict, 梯度\n",
    "    \n",
    "    '''\n",
    "    \n",
    "    # 计算W2的梯度\n",
    "    # YOUR CODE HERE\n",
    "    dW2 = np.dot(H.T, (y_pred - y_true)) / len(y_pred)\n",
    "    \n",
    "    # 计算b2的梯度\n",
    "    # YOUR CODE HERE\n",
    "    db2 = np.sum(y_pred - y_true, axis = 0) / len(y_pred)\n",
    "    \n",
    "    # 计算ReLU的梯度\n",
    "    relu_grad = Z.copy()\n",
    "    relu_grad[relu_grad >= 0] = 1\n",
    "    relu_grad[relu_grad < 0] = 0\n",
    "    \n",
    "    # 计算W1的梯度\n",
    "    # YOUR CODE HERE\n",
    "    dW1 = np.dot(X.T, np.dot(y_pred - y_true, parameters['W2'].T) * relu_grad) / len(y_pred)\n",
    "    \n",
    "    # 计算b1的梯度\n",
    "    # YOUR CODE HERE\n",
    "    db1 = np.sum((np.dot(y_pred - y_true, parameters['W2'].T) * relu_grad), axis = 0) / len(y_pred)\n",
    "    \n",
    "    grads = {'dW2': dW2, 'db2': db2, 'dW1': dW1, 'db1': db1}\n",
    "    \n",
    "    return grads"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**梯度下降，参数更新**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def update(parameters, grads, learning_rate):\n",
    "    '''\n",
    "    参数更新\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    parameters: dict，参数\n",
    "    \n",
    "    grads: dict, 梯度\n",
    "    \n",
    "    learning_rate: float, 学习率\n",
    "    \n",
    "    '''\n",
    "    parameters['W2'] -= learning_rate * grads['dW2']\n",
    "    parameters['b2'] -= learning_rate * grads['db2']\n",
    "    parameters['W1'] -= learning_rate * grads['dW1']\n",
    "    parameters['b1'] -= learning_rate * grads['db1']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def backward(y_true, y_pred, H, Z, X, parameters, learning_rate):\n",
    "    '''\n",
    "    计算梯度，参数更新\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    y_true: np.ndarray，shape = (n, K), 真值\n",
    "    \n",
    "    y_pred: np.ndarray, shape = (n, K)，softmax激活后的输出层的输出值\n",
    "    \n",
    "    H: np.ndarray, shape = (n, h)，隐藏层激活后的值\n",
    "    \n",
    "    Z: np.ndarray, shape = (n, h), 隐藏层激活前的值\n",
    "    \n",
    "    X: np.ndarray, shape = (n, m)，输入的原始数据\n",
    "    \n",
    "    parameters: dict，参数\n",
    "    \n",
    "    learning_rate: float, 学习率\n",
    "    \n",
    "    '''\n",
    "    # 计算梯度\n",
    "    # YOUR CODE HERE\n",
    "    grads = compute_gradient(y_true, y_pred, H, Z, X, parameters)\n",
    "    \n",
    "    # 更新参数\n",
    "    # YOUR CODE HERE\n",
    "    update(parameters, grads, learning_rate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(trainX, trainY, testX, testY, parameters, epochs, learning_rate = 0.01, verbose = False):\n",
    "    '''\n",
    "    训练\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    Parameters\n",
    "    ----------\n",
    "    trainX: np.ndarray, shape = (n, m), 训练集\n",
    "    \n",
    "    trainY: np.ndarray, shape = (n, K), 训练集标记\n",
    "    \n",
    "    testX: np.ndarray, shape = (n_test, m)，测试集\n",
    "    \n",
    "    testY: np.ndarray, shape = (n_test, K)，测试集的标记\n",
    "    \n",
    "    parameters: dict，参数\n",
    "    \n",
    "    epochs: int, 要迭代的轮数\n",
    "    \n",
    "    learning_rate: float, default 0.01，学习率\n",
    "    \n",
    "    verbose: boolean, default False，是否打印损失值\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    training_loss_list: list(float)，每迭代一次之后，训练集上的损失值\n",
    "    \n",
    "    testing_loss_list: list(float)，每迭代一次之后，测试集上的损失值\n",
    "    \n",
    "    '''\n",
    "    # 存储损失值\n",
    "    training_loss_list = []\n",
    "    testing_loss_list = []\n",
    "    \n",
    "    for i in range(epochs):\n",
    "        \n",
    "        # 这里要计算出Z和H，因为后面反向传播计算梯度的时候需要这两个矩阵\n",
    "        Z = linear_combination(trainX, parameters['W1'], parameters['b1'])\n",
    "        H = ReLU(Z)\n",
    "        train_O = linear_combination(H, parameters['W2'], parameters['b2'])\n",
    "        train_y_pred = softmax(train_O)\n",
    "        training_loss = cross_entropy_with_softmax(trainY, train_O)\n",
    "        \n",
    "        test_O = forward(testX, parameters)\n",
    "        testing_loss = cross_entropy_with_softmax(testY, test_O)\n",
    "        \n",
    "        if verbose == True:\n",
    "            print('epoch %s, training loss:%s'%(i + 1, training_loss))\n",
    "            print('epoch %s, testing loss:%s'%(i + 1, testing_loss))\n",
    "            print()\n",
    "        \n",
    "        training_loss_list.append(training_loss)\n",
    "        testing_loss_list.append(testing_loss)\n",
    "        \n",
    "        backward(trainY, train_y_pred, H, Z, trainX, parameters, learning_rate)\n",
    "    return training_loss_list, testing_loss_list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. 绘制模型损失值变化曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_loss_curve(training_loss_list, testing_loss_list):\n",
    "    '''\n",
    "    绘制损失值变化曲线\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    training_loss_list: list(float)，每迭代一次之后，训练集上的损失值\n",
    "    \n",
    "    testing_loss_list: list(float)，每迭代一次之后，测试集上的损失值\n",
    "    \n",
    "    '''\n",
    "    plt.figure(figsize = (10, 6))\n",
    "    plt.plot(training_loss_list, label = 'training loss')\n",
    "    plt.plot(testing_loss_list, label = 'testing loss')\n",
    "    plt.xlabel('epoch')\n",
    "    plt.ylabel('loss')\n",
    "    plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. 预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict(X, parameters):\n",
    "    '''\n",
    "    预测，调用forward函数完成神经网络对输入X的计算，然后完成类别的划分，取每行最大的那个数的下标作为标记\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    X: np.ndarray, shape = (n, m), 训练集\n",
    "    \n",
    "    parameters: dict，参数\n",
    "    \n",
    "    Returns\n",
    "    ----------\n",
    "    prediction: np.ndarray, shape = (n, 1)，预测的标记\n",
    "    \n",
    "    '''\n",
    "    # 用forward函数得到softmax激活前的值\n",
    "    # YOUR CODE HERE\n",
    "    O = forward(X, parameters)\n",
    "    \n",
    "    # 计算softmax激活后的值\n",
    "    # YOUR CODE HERE\n",
    "    y_pred = softmax(O)\n",
    "    \n",
    "    # 取每行最大的元素对应的下标\n",
    "    # YOUR CODE HERE\n",
    "    prediction = np.argmax(y_pred,axis = 1)\n",
    "    \n",
    "    return prediction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 9.模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试样例\n",
    "from sklearn.metrics import accuracy_score\n",
    "from sklearn.metrics import precision_score\n",
    "from sklearn.metrics import recall_score\n",
    "from sklearn.metrics import f1_score\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "training time: 0.7559773921966553 s\n"
     ]
    }
   ],
   "source": [
    "start_time = time()\n",
    "\n",
    "h = 50\n",
    "K = 2\n",
    "parameters = initialize(h, K)\n",
    "training_loss_list, testing_loss_list = train(trainX, trainY_mat, testX, testY_mat, parameters, 1000, 0.03, False)\n",
    "\n",
    "end_time = time()\n",
    "print('training time: %s s'%(end_time - start_time))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "神经网络模型：\n",
      "精度： 0.52 查准率： 0.52 查全率： 1.0 f1： 0.68\n"
     ]
    }
   ],
   "source": [
    "predictiont = predict(testX, parameterst)\n",
    "as_spam_s=round(accuracy_score(testY,predictiont),2)\n",
    "ps_spam_s=round(precision_score(testY,predictiont),2)\n",
    "rs_spam_s=round(recall_score(testY,predictiont),2)\n",
    "f1_spam_s=round(f1_score(testY,predictiont),2)\n",
    "print('神经网络模型：')\n",
    "print('精度：',as_spam_s,'查准率：',ps_spam_s,'查全率：',rs_spam_s,'f1：',f1_spam_s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## heart disease分类任务\n",
    "\n",
    "\n",
    "三层感知机：  \n",
    "\n",
    "精度 | 查准率 | 查全率 | F1\n",
    "- | - | - | -\n",
    "0.52| 0.52 | 1.0 | 0.68"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_loss_curve(training_loss_list, testing_loss_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
